##@file
# @brief uicc package
# @author <libxiangnan@gmail.com>
# @date 2011/11/21
# @version 0.0.1
# @verbatim
# provide package uicc 0.0.1,need sclib and ifd
# @endverbatim
package require ifd
package require sclib
package provide uicc 0.0.1

#@code
##@namespace uicc
# @brief namespace for uicc basic operations.
namespace eval uicc {
    namespace export comment reset assertATR transCAPDU getRData assertRData assertSW

    variable ATR {};#<store ATR get from reset.
    variable ReqAPDU {};#<last request APDU.
    variable RData {};#<update a new value after transmiting an APDU; part of RespAPDU.
    variable SW {};#<update a new value after transmiting an APDU. part of RespAPDU.
    variable LogPath {};#<full file path for storing script log.
    variable Total 0;#<total number of responsing APDU.
    variable Success 0;#<number of sucessful asserts.
    variable Failure 0;#<number of failed asserts.
    variable Error 0;#<number of unexpected asserts result(-1).
}
# @endcode

##@brief works as # except logging it's contents.
# @param[in] args log contents
# @return A boolean flag. If successfully logging, 1 will be returned; or 0 will be returned.
proc uicc::comment {args} {
    variable r "line number" content ""
    append r " : clock format [clock seconds] : "
    foreach ctx $content {
	append content $ctx
    }
    append r $content
}

##@brief notify card resetting and get ATR.
# @param[in] ifdString card IFD. IFD has a default empty value, if IFD is empty, it will find the first IFD on PC.
# @return ATR of current IFD. If meeting error, none will be returned.
proc uicc::reset {{ifdString {}}} {
    variable lcIfdString {}
    if {![string equal $ifdString {}]} {
	set ifds [listIFD]
	if {![llength $ifds]} {
	    return
	} else {
	    set lcIfdString [lindex $ifds 0]
	}
    } else {
	set lcIfdString $ifdString
    }
    if {![reopenIFD]} {
	getATR atr
	return $atr
    } else {
	return
    }
}

##@brief check whether current ATR and expected ATR equal or not
# @param[in] expectATR expected ATR
# @return A int flag. If current ATR is equal will expected ATR, 1 will be returned, or 0 returned.\
If assert before reset, -1 will be returned.
proc uicc::assertATR {expectATR} {
    incr ::uicc::Total
    if {[string equal $::uicc::ATR {}]} {
	incr ::uicc::Error
	return -1
    } else {
	if {[string equal -nocase $expectATR $::uicc::ATR]} {
	    incr ::uicc::Success
	    return 1
	} else {
	    incr ::uicc::Failure
	    return 0
	}
    }
}

##@brief transmit request APDU to the communicated device.
# @param[in] commandAPDU APDU to be sent to device.
# @return response APDU.
# @note ReqAPDU RData SW will be reset.
proc uicc::transCAPDU {commandAPDU} {
    set ::uicc::ReqAPDU {}
    set ::uicc::RData {}
    set ::uicc::SW {}
    if {![::sclib::checkAPDU $commandAPDU]} {
	return
    } else {
	variable lcRAPDU {}
	transmitAPDU $commandAPDU lcRAPDU
	if {![::sclib::checkAPDU $lcRAPDU] || [string length $lcRAPDU] < 4} {
	    return
	} elseif {[string length $lcRAPDU] == 4} {
	    set ::uicc::SW $lcRAPDU
	} else {
	    set lcRAPDULgt [string length $lcRAPDU]
	    set uicc::RData [string range $lcRAPDU 0 [expr $lcRAPDULgt - 5]]
	    set ::uicc::SW [string range $lcRAPDU [expr $lcRAPDULgt - 4] $lcRAPDULgt]
	}
	set ::uicc::ReqAPDU $commandAPDU
	return $lcRAPDU
    }
}

##@brief uicc::RData Getter.
# @return copy of uicc::RData or -1(in this case, User may not successfully transmit an request APDU).
proc uicc::getRData {} {
    if {[string equal $::uicc::SW {}]} {
	return -1
    } else {
	return $uicc::RData
    }
}

##@brief uicc::SW Getter.
# @return copy of uicc::SW or -1(in this case, user may not successfully transmit an request APDU).
proc uicc::getSW {} {
    if {[string equal $::uicc::SW {}]} {
	return -1
    } else {
	return $::uicc::SW
    }
}

##@brief check whether responsing RData equal with expected RData.
# @param[in] expectData expected data part of response APDU.
# @return An int flag. If expcted Data is equal with current Data, 1 will be retured, or 0 retured.\
If invoked before transmiting an APDU successfully, -1 will be returned.
proc uicc::assertRData {expectData} {
    incr ::uicc::Total
    if {[string equal $::uicc::SW {}]} {
	incr ::uicc::Error
	return -1
    } else {
	if {[string equal -nocase $uicc::RData $expectData]} {
	    incr ::uicc::Success
	    return 1
	} else {
	    incr ::uicc::Failure
	    return 0
	}
    }
}

##@brief check whether responsing SW equal with expected SW.
# @param[in] expectSW expected SW part of response APDU.
# @return An int flag. If expcted SW is equal with current SW, 1 will be retured, or 0 retured.\
If invoked before transmiting an APDU successfully, -1 will be returned.
proc uicc::assertSW {expectSW} {
    incr ::uicc::Total
    if {[string equal $::uicc::SW {}]} {
	incr ::uicc::Error
	return -1
    } else {
	if {[string equal -nocase $::uicc::SW $expectSW]} {
	    incr ::uicc::Success
	    return 1
	} else {
	    incr ::uicc::Failure
	    return 0
	}
    }
}

# end --